package me.yricky.mayu.utils.sequence;


import me.yricky.mayu.ui.fasta.FastaSeqModel;
import me.yricky.mayu.utils.misc.FileIntMatrix;
import me.yricky.mayu.utils.misc.IIntMatrix;
import me.yricky.mayu.utils.misc.MemIntMatrix;

import java.util.ArrayList;
import java.util.List;

/**
 * @author Yricky
 * @date 2022/4/21
 */
public final class SeqAliJImpl {
    final FastaSeqModel seq1;
    final FastaSeqModel seq2;

    private final IIntMatrix mat;
    final int row;
    final int col;

    final int bestScore;
    private final PairScore pairScore;
    private final int vScore;

    private float progress = 0f;

    public float getProgress() {
        return progress;
    }

    public int getScore(){
        return result.score;
    }

    public List<Long> getResultIndexes(){
        return result.indexes;
    }

    Runnable progressNotifier;
    private final Result result = new Result();

    public SeqAliJImpl(FastaSeqModel _seq1, FastaSeqModel _seq2, PairScore pairScore, int vacancyScore){
        seq1 = _seq1;
        seq2 = _seq2;
        row = seq1.getSeq().size();
        col = seq2.getSeq().size();
        mat = new MemIntMatrix(row,col);//FileIntMatrix.create(row,col,new File("/tmp/cache.matrix"));
        this.pairScore = pairScore;
        vScore = vacancyScore;
        bestScore = Math.min(row,col) * pairScore.score(' ',' ');
    }

    public int get(int row,int col,int def){
        return mat.get(row,col,def);
    }

    public void run(){
        char c1,c2;
        for(int rowIndex = 0;rowIndex < row;++rowIndex){
            c1 = seq1.getSeq().get(rowIndex);
            progress = rowIndex*1f/row;
            if(progressNotifier != null){
                progressNotifier.run();
            }
            for(int colIndex=0;colIndex<col;++colIndex){
                c2 = seq2.getSeq().get(colIndex);
                var scoreLT = get(rowIndex - 1,colIndex - 1, 0);
                var scoreT = get(rowIndex - 1,colIndex, 0);
                var scoreL = get(rowIndex,colIndex - 1, 0);
                var score = Math.max(Math.max(scoreL + vScore,scoreLT + pairScore.score(c1,c2)),(scoreT + vScore));
                score = Math.max(score,0);
                mat.set(rowIndex,colIndex,score);
                if(score > result.score){
                    result.score = score;
                    result.indexes.clear();
                    result.indexes.add(packIndex(rowIndex,colIndex));
                }else if(score == result.score){
                    result.indexes.add(packIndex(rowIndex,colIndex));
                }
            }
        }
        if(mat instanceof FileIntMatrix){
            ((FileIntMatrix)mat).save();
        }
    }

    static class Result{
        int score = 0;
        final ArrayList<Long> indexes = new ArrayList<>();
    }

    private static long packIndex(int row, int col){
        return (((long)row) << 32) | (((long)col) & 0xffffffffL);
    }

    public static int row(long packedIndex){
        return (int)(packedIndex >> 32);
    }

    public static int col(long packedIndex){
        return (int)(packedIndex & 0xffffffffL);
    }

    interface PairScore{
        int score(char c1,char c2);
    }
}
